use std::collections::HashMap;
use std::convert::Infallible;
use tokio::sync::{mpsc, oneshot};
use tracing::{debug, info, warn};
use warp::{reject, reply, Filter, Reply};

use super::room::{room_task, RoomMsg};
use crate::error::{Error, Result};

use serde_derive::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct Id {
    value: usize,
}
impl Id {
    pub fn new(value: usize) -> Self {
        Id { value }
    }
}

pub(crate) enum Message {
    /// new a room, name, password, room_id
    New(String, Option<String>, oneshot::Sender<usize>),

    /// 加入的时候，通过这个获取
    Get(usize, oneshot::Sender<Option<mpsc::Sender<RoomMsg>>>),

    /// drom room, room id
    Drop(usize),
}

#[derive(Clone)]
pub struct RoomManager {
    sender: mpsc::Sender<Message>,
}

impl RoomManager {
    pub fn new() -> Self {
        let (s, r) = mpsc::channel(32);
        let s_clone = s.clone();
        tokio::spawn(async move { room_manager_task(r, s_clone).await });
        Self { sender: s }
    }
}

pub fn with_room_manager(
    room_manager: RoomManager,
) -> impl Filter<Extract = (RoomManager,), Error = Infallible> + Clone {
    warp::any().map(move || room_manager.clone())
}

/// 管理所有的 房间
async fn room_manager_task(
    mut recevicer: mpsc::Receiver<Message>,
    manager_sender: mpsc::Sender<Message>,
) {
    let mut next_id = 0;
    let mut rooms: HashMap<usize, mpsc::Sender<RoomMsg>> = HashMap::new();

    while let Some(msg) = recevicer.recv().await {
        use Message::*;
        match msg {
            New(name, password, sender) => {
                let (s, r) = mpsc::channel(8);
                let manager_sender = manager_sender.clone();
                tokio::spawn(async move {
                    room_task(name, password, next_id, r, manager_sender).await;
                });
                rooms.insert(next_id, s);

                if let Err(_) = sender.send(next_id) {
                    warn!("room manager send failed");
                };
                next_id += 1;
            }
            Get(id, sender) => {
                if let Err(_) = sender.send(rooms.get(&id).map(|s| s.clone())) {
                    warn!("room manager send failed");
                }
            }
            Drop(id) => {
                rooms.remove(&id);
            }
        }
    }
}

/// 这个不是客户端 api
// TODO: 还有 drop
pub async fn get(id: usize, room_manager: RoomManager) -> Option<mpsc::Sender<RoomMsg>> {
    let (s, r) = oneshot::channel();
    if let Err(_err) = room_manager.sender.send(Message::Get(id, s)).await {
        warn!("room manager get id error");
    }

    if let Ok(v) = r.await {
        v
    } else {
        warn!("get room oneshot recv error, id: {}", id);
        None
    }
}

pub async fn new(
    name: String,
    room_manager: RoomManager,
    password: Option<String>,
) -> Result<impl Reply> {
    debug!("new user get room id");
    let (s, r) = oneshot::channel();
    room_manager
        .sender
        .send(Message::New(name, password, s))
        .await
        .map_err(Error::from)?;

    Ok(r.await
        .map(|id| reply::json(&Id::new(id)))
        .map_err(Error::from)?)
}
